package vcsn.handlers;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.jgit.api.Git;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IStartup;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;

import vcsn.ClientThread;
import vcsn.Config;
import vcsn.Configuration;
import vcsn.ServerThread;
import vcsn.Config.SyncStatus;
import vcsn.entities.FileLock;
import vcsn.entities.LastAppliedCommitHashList;
import vcsn.entities.VectorClock;
import vcsn.utils.ExportImportHandler;

public class StartupHandler implements IStartup {

	@SuppressWarnings("resource")
	@Override
	public void earlyStartup() {
		try {
			IWorkbenchWindow[] windows = PlatformUI.getWorkbench().getWorkbenchWindows();
			for(IWorkbenchWindow window : windows){
				IWorkbenchPage[] workPages = window.getPages();
				if(workPages.length > 0){
					for(IWorkbenchPage page : workPages){
						page.addPartListener(new IPartListener(){

							@Override
							public void partActivated(IWorkbenchPart part) {
							}

							@Override
							public void partBroughtToTop(IWorkbenchPart part) {
							}

							@Override
							public void partClosed(IWorkbenchPart part) {
								if(Config.status == SyncStatus.OFFLINE){
									return;
								}
								try{
									if(part instanceof IEditorPart){
										if(((IEditorPart) part).getEditorInput() instanceof IFileEditorInput){
											IFileEditorInput input = (IFileEditorInput)(((IEditorPart) part).getEditorInput());
											IFile file = input.getFile();
											IProject activeProject = file.getProject();
											if(new File(activeProject.getLocation().toString() + File.separator + Config.peerFile).exists()){
												Configuration configure = new Configuration(activeProject.getLocation().toString(), activeProject.getName());
												FileLock fileLock = new FileLock(activeProject.getLocation().toString() + File.separator + Config.lockFile);
												List<String> lockedFileList = fileLock.readLockedFileList();
												if(lockedFileList.contains(file.getProjectRelativePath().toString() + Config.separator + configure.getUserName())){
													int i = lockedFileList.indexOf(file.getProjectRelativePath().toString() + Config.separator + configure.getUserName());
													lockedFileList.remove(i);
													fileLock.writeLockedFileList(lockedFileList);

													VectorClock localClock = ExportImportHandler.importVectorClock(System.getProperty(Config.homeDirectoryNotation) + File.separator + Config.localClockFile, activeProject.getName());
													if(localClock != null){
														localClock.incrementClock(configure.getUserName() + Config.keyConnector + configure.getUserEmail());
													}else{
														localClock = new VectorClock();
														localClock.incrementClock(configure.getUserName() + Config.keyConnector + configure.getUserEmail());
													}
													ExportImportHandler.exportVectorClock(localClock, activeProject.getName());

													//commit for the lock file updated
													Git git = Git.open(new File(activeProject.getLocation().toString()));
													git.add().addFilepattern(Config.lockFile).call();
													git.commit().setAuthor(configure.getUserName(), configure.getUserEmail()).setMessage(Config.lockFileUpdateMsg).call();
													git.push().setRemote(configure.getRemoteURI()).setForce(true).call();
													
													updateLastAppliedCommitHash(System.getProperty(Config.homeDirectoryNotation) + File.separator + Config.lastAppliedCommitHashListFile, configure.getUserName(), git.getRepository().getRef(Config.HEADHash).getObjectId().getName(), activeProject.getName());

													//new ClientThread(new File(activeProject.getLocation().toString() + File.separator + Config.lockFile), activeProject.getProject().getName(),ExportImportHandler.importVectorClock(System.getProperty(Config.homeDirectoryNotation) + File.separator + Config.localClockFile),new File(activeProject.getLocation().toString() + File.separator + Config.peerFile), Config.HeaderType.FILELOCK);
													ClientThread client = new ClientThread();
													String localAddress = client.getLocalAddress(new File(activeProject.getProject().getLocation().toString() + File.separator + Config.peerFile));
													client.writeHeaderIntoPackageFile(activeProject.getProject().getName(), ExportImportHandler.importVectorClock(System.getProperty(Config.homeDirectoryNotation) + File.separator + Config.localClockFile, activeProject.getName()), localAddress, Config.HeaderType.FILELOCK);
													client.writeFileIntoPackageFile(new File(activeProject.getLocation().toString() + File.separator + Config.lockFile));
													client.writeStringIntoPackageFile(configure.getUserName());
													client.writeStringIntoPackageFile(git.getRepository().getRef(Config.HEADHash).getObjectId().getName());
													client.flush(new File(activeProject.getLocation().toString() + File.separator + Config.peerFile));
													client.disposePackage();
												}
											}
										}
									}
								}catch(Exception e){
									e.printStackTrace();
								}
							}

							@Override
							public void partDeactivated(IWorkbenchPart part) {
							}

							@Override
							public void partOpened(IWorkbenchPart part) {
//								if(Config.status == SyncStatus.OFFLINE){
//									return;
//								}
								if(part instanceof IEditorPart){
									if(((IEditorPart) part).getEditorInput() instanceof IFileEditorInput){
										IFileEditorInput input = (IFileEditorInput)(((IEditorPart) part).getEditorInput());
										IFile file = input.getFile();
										IProject activeProject = file.getProject();
										if(new File(activeProject.getLocation().toString() + File.separator + Config.peerFile).exists()){
											Configuration configure = new Configuration(activeProject.getLocation().toString(), activeProject.getName());
											File lockfile = new File(activeProject.getLocation().toString() + File.separator + Config.lockFile);
											try{
												if(!lockfile.exists()){
													if(Config.status == SyncStatus.OFFLINE){
														return;
													}
													lockfile.createNewFile();
													FileWriter fw = new FileWriter(lockfile, false);
													fw.write(file.getProjectRelativePath().toString() + Config.separator + configure.getUserName() +  "\n");
													fw.flush();
													fw.close();

													VectorClock localClock = ExportImportHandler.importVectorClock(System.getProperty(Config.homeDirectoryNotation) + File.separator + Config.localClockFile, activeProject.getName());
													if(localClock != null){
														localClock.incrementClock(configure.getUserName() + Config.keyConnector + configure.getUserEmail());
													}else{
														localClock = new VectorClock();
														localClock.incrementClock(configure.getUserName() + Config.keyConnector + configure.getUserEmail());
													}
													ExportImportHandler.exportVectorClock(localClock, activeProject.getName());

													//commit for the lock file updated
													Git git = Git.open(new File(activeProject.getLocation().toString()));
													git.add().addFilepattern(Config.lockFile).call();
													git.commit().setAuthor(configure.getUserName(), configure.getUserEmail()).setMessage(Config.lockFileUpdateMsg).call();
													git.push().setRemote(configure.getRemoteURI()).setForce(true).call();
													
													updateLastAppliedCommitHash(System.getProperty(Config.homeDirectoryNotation) + File.separator + Config.lastAppliedCommitHashListFile, configure.getUserName(), git.getRepository().getRef(Config.HEADHash).getObjectId().getName(), activeProject.getName());
													
													//new ClientThread(lockfile, activeProject.getProject().getName(),localClock,new File(activeProject.getLocation().toString() + File.separator + Config.peerFile), Config.HeaderType.FILELOCK);
													ClientThread client = new ClientThread();
													String localAddress = client.getLocalAddress(new File(activeProject.getProject().getLocation().toString() + File.separator + Config.peerFile));
													client.writeHeaderIntoPackageFile(activeProject.getProject().getName(), localClock, localAddress, Config.HeaderType.FILELOCK);
													client.writeFileIntoPackageFile(lockfile);
													client.writeStringIntoPackageFile(configure.getUserName());
													client.writeStringIntoPackageFile(git.getRepository().getRef(Config.HEADHash).getObjectId().getName());
													client.flush(new File(activeProject.getLocation().toString() + File.separator + Config.peerFile));
													client.disposePackage();
												}else{
													FileLock fileLock = new FileLock(lockfile.getAbsolutePath());
													List<String> lockedFileList = fileLock.readLockedFileList();
													boolean isLocker = true;
													for(String lockEntry : lockedFileList){
														String[] fileLockerPair = lockEntry.split(Config.separator);
														if(fileLockerPair[0].equals(file.getProjectRelativePath().toString())){
															isLocker = false;
															if(fileLockerPair[1].equals(configure.getUserName())){
																isLocker = true;
															}
														}
													}
													if(!isLocker){
														IWorkbench workbench = PlatformUI.getWorkbench();
														workbench.getActiveWorkbenchWindow().getActivePage().closeEditor((IEditorPart) part, true);
													}else if(!lockedFileList.contains(file.getProjectRelativePath().toString() + Config.separator + configure.getUserName())){
														if(Config.status == SyncStatus.OFFLINE){
															return;
														}
														lockedFileList.add(file.getProjectRelativePath().toString() + Config.separator + configure.getUserName());
														fileLock.writeLockedFileList(lockedFileList);

														VectorClock localClock = ExportImportHandler.importVectorClock(System.getProperty(Config.homeDirectoryNotation) + File.separator + Config.localClockFile, activeProject.getName());
														if(localClock != null){
															localClock.incrementClock(configure.getUserName() + Config.keyConnector + configure.getUserEmail());
														}else{
															localClock = new VectorClock();
															localClock.incrementClock(configure.getUserName() + Config.keyConnector + configure.getUserEmail());
														}
														ExportImportHandler.exportVectorClock(localClock, activeProject.getName());

														//commit for the lock file updated
														Git git = Git.open(new File(activeProject.getLocation().toString()));
														git.add().addFilepattern(Config.lockFile).call();
														git.commit().setAuthor(configure.getUserName(), configure.getUserEmail()).setMessage(Config.lockFileUpdateMsg).call();
														git.push().setRemote(configure.getRemoteURI()).setForce(true).call();
														
														updateLastAppliedCommitHash(System.getProperty(Config.homeDirectoryNotation) + File.separator + Config.lastAppliedCommitHashListFile, configure.getUserName(), git.getRepository().getRef(Config.HEADHash).getObjectId().getName(), activeProject.getName());
														
														//new ClientThread(lockfile, activeProject.getProject().getName(),localClock,new File(activeProject.getLocation().toString() + File.separator + Config.peerFile), Config.HeaderType.FILELOCK);
														ClientThread client = new ClientThread();
														String localAddress = client.getLocalAddress(new File(activeProject.getProject().getLocation().toString() + File.separator + Config.peerFile));
														client.writeHeaderIntoPackageFile(activeProject.getProject().getName(), localClock, localAddress, Config.HeaderType.FILELOCK);
														client.writeFileIntoPackageFile(lockfile);
														client.writeStringIntoPackageFile(configure.getUserName());
														client.writeStringIntoPackageFile(git.getRepository().getRef(Config.HEADHash).getObjectId().getName());
														client.flush(new File(activeProject.getLocation().toString() + File.separator + Config.peerFile));
														client.disposePackage();
													}
												}
											}catch(Exception ex){
												ex.printStackTrace();
											}
										}
									}

								}
							}
						});
					}
				}
			}
			ServerSocket ss = new ServerSocket(14428);
			System.out.println("running server ...");
			while(true) new ServerThread(ss.accept());
			
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			
		}
		System.out.println("starting ...");
	}
	
	private void updateLastAppliedCommitHash(String fileLocation, String commitAuthor, String incomingCommitHashString, String projectName){
		LastAppliedCommitHashList commithashList = ExportImportHandler.importLastAppliedCommitHashList(fileLocation, projectName);
		if(commithashList == null){
			commithashList = new LastAppliedCommitHashList();
		}
		commithashList.setCommitHash(commitAuthor, incomingCommitHashString);
		ExportImportHandler.exportLastAppliedCommitHashList(commithashList, projectName);
	}
}
